//! Protocol Buffers Actor Message Example
//!
//! This example demonstrates how to use Protocol Buffers for actor message serialization.
//! It shows how to:
//! 1. Define Protocol Buffer message types
//! 2. Convert between Protocol Buffer messages and actor messages
//! 3. Measure serialization size comparison with MessagePack
//!
//! Run with:
//! ```bash
//! cargo run --example protobuf_actor_message --features "remote protobuf"
//! ```

#[cfg(all(feature = "remote", feature = "protobuf"))]
mod example {
    use kameo::serialization::{self, SerializationConfig, SerializationFormat};
    use prost::Message as ProstMessage;
    use serde::{Deserialize, Serialize};
    
    // Define Protocol Buffer message for a simple counter command
    #[derive(Clone, ProstMessage)]
    pub struct CounterCommandProto {
        #[prost(uint32, tag = "1")]
        pub amount: u32,
        
        #[prost(string, tag = "2")]
        pub description: String,
        
        #[prost(bool, tag = "3")]
        pub is_increment: bool,
    }
    
    // Define the actor message (compatible with serde)
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct CounterCommand {
        amount: u32,
        description: String,
        is_increment: bool,
    }
    
    // Implement conversion between Protocol Buffer and actor message
    impl From<CounterCommandProto> for CounterCommand {
        fn from(proto: CounterCommandProto) -> Self {
            Self {
                amount: proto.amount,
                description: proto.description,
                is_increment: proto.is_increment,
            }
        }
    }
    
    impl From<CounterCommand> for CounterCommandProto {
        fn from(cmd: CounterCommand) -> Self {
            Self {
                amount: cmd.amount,
                description: cmd.description,
                is_increment: cmd.is_increment,
            }
        }
    }
    
    // Define Protocol Buffer for response
    #[derive(Clone, ProstMessage)]
    pub struct CounterResponseProto {
        #[prost(int64, tag = "1")]
        pub new_value: i64,
        
        #[prost(string, tag = "2")]
        pub message: String,
        
        #[prost(bool, tag = "3")]
        pub success: bool,
    }
    
    // Actor response (compatible with serde)
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct CounterResponse {
        new_value: i64,
        message: String,
        success: bool,
    }
    
    // Implement conversion between Protocol Buffer and actor response
    impl From<CounterResponseProto> for CounterResponse {
        fn from(proto: CounterResponseProto) -> Self {
            Self {
                new_value: proto.new_value,
                message: proto.message,
                success: proto.success,
            }
        }
    }
    
    impl From<CounterResponse> for CounterResponseProto {
        fn from(resp: CounterResponse) -> Self {
            Self {
                new_value: resp.new_value,
                message: resp.message,
                success: resp.success,
            }
        }
    }
    
    pub fn run() {
        println!("Protocol Buffers Actor Message Example\n");
        
        // Example actor state - a simple counter
        let mut counter_value = 0;
        
        // Create the messages
        let increment_cmd = CounterCommand {
            amount: 42,
            description: "Increment the counter".to_string(),
            is_increment: true,
        };
        
        let decrement_cmd = CounterCommand {
            amount: 10,
            description: "Decrement the counter".to_string(),
            is_increment: false,
        };
        
        // 1. Protocol Buffers Serialization
        println!("--- Protocol Buffers Serialization ---");
        
        // Convert to Protocol Buffer format first
        let increment_proto: CounterCommandProto = increment_cmd.clone().into();
        
        // Serialize to binary
        let proto_bytes = increment_proto.encode_to_vec();
        println!("Protocol Buffer serialized size: {} bytes", proto_bytes.len());
        println!("Hex: {}", hex_string(&proto_bytes));
        
        // Deserialize back from binary
        let decoded_proto = CounterCommandProto::decode(proto_bytes.as_slice()).unwrap();
        
        // Convert back to actor message
        let decoded_cmd: CounterCommand = decoded_proto.into();
        println!("Decoded command: amount={}, description='{}', is_increment={}", 
                 decoded_cmd.amount, decoded_cmd.description, decoded_cmd.is_increment);
        
        // 2. MessagePack Serialization (for comparison)
        println!("\n--- MessagePack Serialization ---");
        
        // Configure to use MessagePack
        let messagepack_config = SerializationConfig::new()
            .with_format(SerializationFormat::MessagePack);
        serialization::init_serialization(messagepack_config);
        
        // Serialize using MessagePack
        let messagepack_bytes = serialization::serialize(&increment_cmd).unwrap();
        println!("MessagePack serialized size: {} bytes", messagepack_bytes.len());
        println!("Hex: {}", hex_string(&messagepack_bytes));
        
        // Deserialize using MessagePack
        let messagepack_decoded: CounterCommand = serialization::deserialize(&messagepack_bytes).unwrap();
        println!("Decoded command: amount={}, description='{}', is_increment={}", 
                 messagepack_decoded.amount, messagepack_decoded.description, messagepack_decoded.is_increment);
        
        // 3. Simulate actor message handling
        println!("\n--- Simulated Actor Message Handling ---");
        
        // Process increment command
        if increment_cmd.is_increment {
            counter_value += increment_cmd.amount as i64;
        } else {
            counter_value -= increment_cmd.amount as i64;
        }
        
        let increment_response = CounterResponse {
            new_value: counter_value,
            message: format!("Counter incremented by {}", increment_cmd.amount),
            success: true,
        };
        
        // Serialize response with Protocol Buffers
        let response_proto: CounterResponseProto = increment_response.clone().into();
        let response_proto_bytes = response_proto.encode_to_vec();
        println!("Response Protocol Buffer size: {} bytes", response_proto_bytes.len());
        
        // Process decrement command
        if decrement_cmd.is_increment {
            counter_value += decrement_cmd.amount as i64;
        } else {
            counter_value -= decrement_cmd.amount as i64;
        }
        
        let decrement_response = CounterResponse {
            new_value: counter_value,
            message: format!("Counter decremented by {}", decrement_cmd.amount),
            success: true,
        };
        
        // Serialize with MessagePack for comparison
        let response_mp_bytes = serialization::serialize(&decrement_response).unwrap();
        println!("Response MessagePack size: {} bytes", response_mp_bytes.len());
        
        // Size comparison
        println!("\n--- Size Comparison ---");
        println!("Command - Protocol Buffers size: {} bytes", proto_bytes.len());
        println!("Command - MessagePack size: {} bytes", messagepack_bytes.len());
        println!("Response - Protocol Buffers size: {} bytes", response_proto_bytes.len());
        println!("Response - MessagePack size: {} bytes", response_mp_bytes.len());
        println!("Average size ratio (ProtoBuf:MessagePack): {:.2}x", 
                 (proto_bytes.len() as f64 / messagepack_bytes.len() as f64 + 
                  response_proto_bytes.len() as f64 / response_mp_bytes.len() as f64) / 2.0);
    }
    
    // Helper function to display binary data as hex
    fn hex_string(bytes: &[u8]) -> String {
        bytes.iter()
            .map(|b| format!("{:02x}", b))
            .collect::<Vec<_>>()
            .join("")
    }
}

fn main() {
    #[cfg(all(feature = "remote", feature = "protobuf"))]
    example::run();
    
    #[cfg(not(all(feature = "remote", feature = "protobuf")))]
    println!("This example requires both 'remote' and 'protobuf' features. Run with:\ncargo run --example protobuf_actor_message --features \"remote protobuf\"");
} 